import type { Layer } from '@shared/schema'
import { Image as ImageIcon, Maximize2, ZoomIn, ZoomOut } from 'lucide-react'
import { forwardRef, useEffect, useImperativeHandle, useRef, useState } from 'react'
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'

interface CanvasProps {
  canvasWidth: number
  canvasHeight: number
  layers: Layer[]
  selectedLayerId: string | null
  onLayerSelect: (layerId: string | null) => void
  onLayerUpdate: (layerId: string, updates: Partial<Layer>) => void
  originalImage?: string
}

export interface CanvasRef {
  getCanvasDataURL: () => Promise<string | null>
}

export const Canvas = forwardRef<CanvasRef, CanvasProps>(({
  canvasWidth,
  canvasHeight,
  layers,
  selectedLayerId,
  onLayerSelect,
  onLayerUpdate,
  originalImage,
}, ref) => {
  const [zoom, setZoom] = useState(1)
  const [editingLayerId, setEditingLayerId] = useState<string | null>(null)
  const [editingText, setEditingText] = useState('')
  const canvasRef = useRef<HTMLCanvasElement>(null)
  const canvasContainerRef = useRef<HTMLDivElement>(null)
  const [isDragging, setIsDragging] = useState(false)
  const [dragStart, setDragStart] = useState({ x: 0, y: 0 })
  const [isResizing, setIsResizing] = useState(false)
  const [resizeHandle, setResizeHandle] = useState<string | null>(null)
  const animationFrameRef = useRef<number | null>(null)

  const handleZoomIn = () => setZoom(z => Math.min(z + 0.25, 3))
  const handleZoomOut = () => setZoom(z => Math.max(z - 0.25, 0.25))
  const handleFitToScreen = () => setZoom(1)

  // 将图层渲染到 canvas 上下文
  const renderLayerToCanvas = async (ctx: CanvasRenderingContext2D, layer: Layer) => {
    const { x, y, width, height } = layer

    ctx.save()

    // 设置透明度
    if (layer.opacity !== undefined) {
      ctx.globalAlpha = layer.opacity
    }

    switch (layer.type) {
      case 'text': {
        const fontSize = layer.fontSize || '16px'
        const fontFamily = layer.fontFamily || 'sans-serif'
        const color = layer.color || '#000000'
        const fontWeight = layer.fontWeight || '400'
        const textAlign = layer.textAlign || 'left'

        ctx.fillStyle = color
        ctx.font = `normal ${fontWeight} ${fontSize} ${fontFamily}`
        ctx.textAlign = textAlign as CanvasTextAlign
        ctx.textBaseline = 'top'

        const lines = layer.content.split('\n')
        const lineHeight = Number.parseInt(fontSize.toString()) * Number.parseFloat(layer.style?.lineHeight || '1.2')

        lines.forEach((line, index) => {
          let textX = x
          if (textAlign === 'center') {
            textX = x + width / 2
          }
          else if (textAlign === 'right') {
            textX = x + width
          }
          ctx.fillText(line, textX, y + lineHeight * (index + 1))
        })
        break
      }

      case 'shape': {
        const fill = layer.fill || '#CCCCCC'
        const stroke = layer.stroke || '#000000'
        const strokeWidth = layer.strokeWidth || 1
        const cornerRadius = layer.cornerRadius || 0
        const shapeType = layer.shapeType || 'rectangle'

        switch (shapeType) {
          case 'rectangle':
            if (fill && fill !== 'transparent') {
              ctx.fillStyle = fill
              if (cornerRadius > 0) {
                ctx.beginPath()
                ctx.roundRect(x, y, width, height, cornerRadius)
                ctx.fill()
              }
              else {
                ctx.fillRect(x, y, width, height)
              }
            }
            if (stroke && stroke !== 'transparent' && strokeWidth > 0) {
              ctx.strokeStyle = stroke
              ctx.lineWidth = strokeWidth
              if (cornerRadius > 0) {
                ctx.beginPath()
                ctx.roundRect(x, y, width, height, cornerRadius)
                ctx.stroke()
              }
              else {
                ctx.strokeRect(x, y, width, height)
              }
            }
            break

          case 'circle':
          case 'ellipse':
            ctx.beginPath()
            ctx.ellipse(x + width / 2, y + height / 2, width / 2, height / 2, 0, 0, 2 * Math.PI)
            if (fill && fill !== 'transparent') {
              ctx.fillStyle = fill
              ctx.fill()
            }
            if (stroke && stroke !== 'transparent' && strokeWidth > 0) {
              ctx.strokeStyle = stroke
              ctx.lineWidth = strokeWidth
              ctx.stroke()
            }
            break

          case 'line':
            if (stroke && stroke !== 'transparent' && strokeWidth > 0) {
              ctx.strokeStyle = stroke
              ctx.lineWidth = strokeWidth
              ctx.beginPath()
              ctx.moveTo(x, y + height / 2)
              ctx.lineTo(x + width, y + height / 2)
              ctx.stroke()
            }
            break
        }
        break
      }

      case 'image': {
        if (layer.imageData) {
          await new Promise<void>((resolve) => {
            const img = new Image()
            img.crossOrigin = 'anonymous'
            img.onload = () => {
              ctx.drawImage(img, x, y, width, height)
              resolve()
            }
            img.onerror = () => resolve()
            img.src = layer.imageData!
          })
        }
        break
      }
    }

    ctx.restore()
  }

  // 暴露导出方法
  useImperativeHandle(ref, () => ({
    getCanvasDataURL: async () => {
      if (!canvasRef.current)
        return null

      const exportCanvas = document.createElement('canvas')
      exportCanvas.width = canvasWidth
      exportCanvas.height = canvasHeight
      const ctx = exportCanvas.getContext('2d')
      if (!ctx)
        return null

      // 绘制原始图片
      if (originalImage) {
        await new Promise<void>((resolve) => {
          const img = new Image()
          img.crossOrigin = 'anonymous'
          img.onload = () => {
            ctx.drawImage(img, 0, 0, canvasWidth, canvasHeight)
            resolve()
          }
          img.onerror = () => resolve()
          img.src = originalImage
        })
      }
      else {
        // 填充白色背景
        ctx.fillStyle = '#ffffff'
        ctx.fillRect(0, 0, canvasWidth, canvasHeight)
      }

      // 绘制所有可见图层
      for (const layer of layers.filter(l => l.visible !== false)) {
        await renderLayerToCanvas(ctx, layer)
      }

      return exportCanvas.toDataURL('image/png')
    },
  }))

  // 渲染图层到隐藏 canvas（用于预览）
  useEffect(() => {
    if (!canvasRef.current)
      return

    const canvas = canvasRef.current
    const ctx = canvas.getContext('2d')
    if (!ctx)
      return

    canvas.width = canvasWidth
    canvas.height = canvasHeight

    ctx.clearRect(0, 0, canvasWidth, canvasHeight)

    // 异步渲染所有图层
    layers.filter(l => l.visible !== false).forEach((layer) => {
      renderLayerToCanvas(ctx, layer)
    })
  }, [layers, canvasWidth, canvasHeight])

  const handleLayerClick = (layerId: string, e: React.MouseEvent) => {
    e.stopPropagation()
    onLayerSelect(layerId)
  }

  // 获取鼠标相对于画布的位置（考虑缩放）
  const getCanvasPosition = (e: React.MouseEvent): { x: number, y: number } | null => {
    if (!canvasContainerRef.current)
      return null

    const container = canvasContainerRef.current
    const rect = container.getBoundingClientRect()

    // 计算鼠标相对于容器的位置
    const mouseX = e.clientX - rect.left
    const mouseY = e.clientY - rect.top

    // 获取缩放后的画布容器
    const scaledContainer = container.querySelector('[style*="transform"]') as HTMLElement
    if (!scaledContainer)
      return null

    const scaledRect = scaledContainer.getBoundingClientRect()
    const containerRect = container.getBoundingClientRect()

    // 计算缩放后的画布在容器中的偏移
    const offsetX = (scaledRect.left - containerRect.left) + (scaledRect.width - canvasWidth * zoom) / 2
    const offsetY = (scaledRect.top - containerRect.top) + (scaledRect.height - canvasHeight * zoom) / 2

    // 转换为画布坐标（考虑缩放）
    const canvasX = (mouseX - offsetX) / zoom
    const canvasY = (mouseY - offsetY) / zoom

    return { x: canvasX, y: canvasY }
  }

  const handleMouseDown = (layerId: string, e: React.MouseEvent) => {
    // 如果正在编辑文本，不触发拖动
    if (editingLayerId === layerId)
      return

    e.stopPropagation()
    const layer = layers.find(l => l.id === layerId)
    if (!layer)
      return

    const canvasPos = getCanvasPosition(e)
    if (!canvasPos)
      return

    const { x, y } = canvasPos
    const { x: layerX, y: layerY } = layer

    // 检查是否点击了调整大小控制点
    const handleSize = 10 / zoom // 根据缩放调整控制点的判定范围
    const isNearBottomRight = Math.abs(x - (layerX + layer.width)) < handleSize
      && Math.abs(y - (layerY + layer.height)) < handleSize

    if (isNearBottomRight) {
      setIsResizing(true)
      setResizeHandle('se')
      setDragStart({ x, y })
      onLayerSelect(layerId)
      return
    }

    // 检查鼠标是否在图层范围内
    if (x >= layerX && x <= layerX + layer.width && y >= layerY && y <= layerY + layer.height) {
      setIsDragging(true)
      setDragStart({
        x: x - layerX,
        y: y - layerY,
      })
      onLayerSelect(layerId)
    }
  }

  const handleMouseMove = (e: React.MouseEvent) => {
    if (!isDragging && !isResizing)
      return
    if (!selectedLayerId)
      return

    // 取消之前的动画帧
    if (animationFrameRef.current) {
      cancelAnimationFrame(animationFrameRef.current)
    }

    // 使用 RAF 优化性能
    animationFrameRef.current = requestAnimationFrame(() => {
      const canvasPos = getCanvasPosition(e)
      if (!canvasPos)
        return

      const layer = layers.find(l => l.id === selectedLayerId)
      if (!layer)
        return

      const { x: mouseX, y: mouseY } = canvasPos

      // 处理拖动
      if (isDragging) {
        const newX = Math.max(0, Math.min(mouseX - dragStart.x, canvasWidth - layer.width))
        const newY = Math.max(0, Math.min(mouseY - dragStart.y, canvasHeight - layer.height))

        onLayerUpdate(selectedLayerId, {
          x: Math.round(newX),
          y: Math.round(newY),
        })
      }

      // 处理调整大小
      if (isResizing && resizeHandle === 'se') {
        const newWidth = Math.max(20, Math.min(mouseX - layer.x, canvasWidth - layer.x))
        const newHeight = Math.max(20, Math.min(mouseY - layer.y, canvasHeight - layer.y))

        onLayerUpdate(selectedLayerId, {
          width: Math.round(newWidth),
          height: Math.round(newHeight),
        })
      }
    })
  }

  const handleMouseUp = () => {
    setIsDragging(false)
    setIsResizing(false)
    setResizeHandle(null)
    // 清理动画帧
    if (animationFrameRef.current) {
      cancelAnimationFrame(animationFrameRef.current)
      animationFrameRef.current = null
    }
  }

  // 清理动画帧
  useEffect(() => {
    return () => {
      if (animationFrameRef.current) {
        cancelAnimationFrame(animationFrameRef.current)
      }
    }
  }, [])

  const handleTextLayerDoubleClick = (layer: Layer) => {
    setEditingLayerId(layer.id)
    setEditingText(layer.content)
  }

  const handleTextEditComplete = () => {
    if (editingLayerId && editingText !== '') {
      onLayerUpdate(editingLayerId, { content: editingText })
    }
    setEditingLayerId(null)
    setEditingText('')
  }

  const handleTextEditKeyDown = (e: React.KeyboardEvent) => {
    if (e.key === 'Enter' && !e.shiftKey) {
      e.preventDefault()
      handleTextEditComplete()
    }
    else if (e.key === 'Escape') {
      setEditingLayerId(null)
      setEditingText('')
    }
  }

  const renderTextLayer = (layer: Layer) => {
    if (layer.type !== 'text')
      return null

    const isSelected = layer.id === selectedLayerId
    const isEditing = layer.id === editingLayerId
    const isActivelyDragging = (isDragging || isResizing) && selectedLayerId === layer.id

    // 从 style 或直接属性中获取样式
    const fontSize = layer.fontSize || (layer.style?.fontSize ? Number.parseInt(layer.style.fontSize) : 16)
    const fontFamily = layer.fontFamily || layer.style?.fontFamily || 'sans-serif'
    const color = layer.color || layer.style?.color || '#000000'
    const fontWeight = layer.fontWeight || layer.style?.fontWeight || 'normal'
    const textAlign = layer.textAlign || layer.style?.textAlign || 'left'

    return (
      <div
        key={layer.id}
        className={`absolute select-none ${
          !isActivelyDragging ? 'transition-opacity' : ''
        } ${
          isDragging && selectedLayerId === layer.id ? 'cursor-grabbing' : 'cursor-grab'
        } ${
          isSelected ? 'ring-2 ring-primary ring-offset-2' : ''
        }`}
        style={{
          left: `${layer.x}px`,
          top: `${layer.y}px`,
          width: `${layer.width}px`,
          minHeight: `${layer.height}px`,
          pointerEvents: isEditing ? 'auto' : 'all',
          willChange: isActivelyDragging ? 'transform' : 'auto',
        }}
        onClick={e => handleLayerClick(layer.id, e)}
        onMouseDown={e => handleMouseDown(layer.id, e)}
        onDoubleClick={() => handleTextLayerDoubleClick(layer as any)}
        data-testid={`text-layer-${layer.id}`}
      >
        {isEditing
          ? (
              <Input
                value={editingText}
                onChange={e => setEditingText(e.target.value)}
                onBlur={handleTextEditComplete}
                onKeyDown={handleTextEditKeyDown}
                autoFocus
                className="border-2 border-primary"
                style={{
                  fontSize: `${fontSize}px`,
                  fontFamily,
                  color,
                  fontWeight,
                  textAlign: textAlign as any,
                }}
                data-testid={`text-layer-input-${layer.id}`}
              />
            )
          : (
              <div
                style={{
                  fontSize: `${fontSize}px`,
                  fontFamily,
                  color,
                  fontWeight,
                  textAlign: textAlign as any,
                  whiteSpace: 'pre-wrap',
                  wordBreak: 'break-word',
                }}
              >
                {layer.content}
              </div>
            )}
        {isSelected && !isEditing && (
          <>
            <div className="absolute -top-6 left-0 bg-primary text-primary-foreground text-xs px-2 py-1 rounded-md whitespace-nowrap font-medium">
              {layer.name || '文本图层'}
              {' '}
              (双击编辑)
            </div>
            {/* 调整大小控制点 */}
            <div
              className="absolute -bottom-1 -right-1 w-3 h-3 bg-primary border-2 border-background rounded-full cursor-se-resize z-10"
              onMouseDown={e => handleMouseDown(layer.id, e)}
            />
          </>
        )}
      </div>
    )
  }

  const renderShapeLayer = (layer: Layer) => {
    if (layer.type !== 'shape')
      return null

    const isSelected = layer.id === selectedLayerId
    const isActivelyDragging = (isDragging || isResizing) && selectedLayerId === layer.id

    // 从 style 或直接属性中获取样式
    const fill = layer.fill || layer.style?.backgroundColor || '#CCCCCC'
    const stroke = layer.stroke || layer.style?.borderColor || 'transparent'
    const strokeWidth = layer.strokeWidth || (layer.style?.borderWidth ? Number.parseInt(layer.style.borderWidth) : 0)
    const cornerRadius = layer.cornerRadius || (layer.style?.borderRadius ? Number.parseInt(layer.style.borderRadius) : 0)
    const shapeType = layer.shapeType || 'rectangle'

    let shapeElement

    switch (shapeType) {
      case 'rectangle':
        shapeElement = (
          <div
            className="w-full h-full"
            style={{
              backgroundColor: fill === 'transparent' ? 'transparent' : fill,
              border: `${strokeWidth}px solid ${stroke === 'transparent' ? 'transparent' : stroke}`,
              borderRadius: `${cornerRadius}px`,
            }}
          />
        )
        break

      case 'circle':
      case 'ellipse':
        shapeElement = (
          <div
            className="w-full h-full"
            style={{
              backgroundColor: fill === 'transparent' ? 'transparent' : fill,
              border: `${strokeWidth}px solid ${stroke === 'transparent' ? 'transparent' : stroke}`,
              borderRadius: '50%',
            }}
          />
        )
        break

      case 'line':
        shapeElement = (
          <div
            className="w-full"
            style={{
              height: `${strokeWidth}px`,
              backgroundColor: stroke === 'transparent' ? 'transparent' : stroke,
              position: 'absolute',
              top: '50%',
              transform: 'translateY(-50%)',
            }}
          />
        )
        break

      default:
        shapeElement = (
          <div
            className="w-full h-full"
            style={{
              backgroundColor: fill === 'transparent' ? 'transparent' : fill,
              border: `${strokeWidth}px solid ${stroke === 'transparent' ? 'transparent' : stroke}`,
            }}
          />
        )
    }

    return (
      <div
        key={layer.id}
        className={`absolute select-none ${
          !isActivelyDragging ? 'transition-opacity' : ''
        } ${
          isDragging && selectedLayerId === layer.id ? 'cursor-grabbing' : 'cursor-grab'
        } ${
          isSelected ? 'ring-2 ring-primary ring-offset-2' : ''
        }`}
        style={{
          left: `${layer.x}px`,
          top: `${layer.y}px`,
          width: `${layer.width}px`,
          height: `${layer.height}px`,
          willChange: isActivelyDragging ? 'transform' : 'auto',
        }}
        onClick={e => handleLayerClick(layer.id, e)}
        onMouseDown={e => handleMouseDown(layer.id, e)}
        data-testid={`shape-layer-${layer.id}`}
      >
        {shapeElement}
        {isSelected && (
          <>
            <div className="absolute -top-6 left-0 bg-primary text-primary-foreground text-xs px-2 py-1 rounded-md whitespace-nowrap font-medium z-10">
              {layer.name || '形状图层'}
            </div>
            {/* 调整大小控制点 */}
            <div
              className="absolute -bottom-1 -right-1 w-3 h-3 bg-primary border-2 border-background rounded-full cursor-se-resize z-10"
              onMouseDown={e => handleMouseDown(layer.id, e)}
            />
          </>
        )}
      </div>
    )
  }

  const renderImageLayer = (layer: Layer) => {
    if (layer.type !== 'image')
      return null

    const isSelected = layer.id === selectedLayerId
    const isActivelyDragging = (isDragging || isResizing) && selectedLayerId === layer.id
    const hasImageData = !!layer.imageData
    const opacity = layer.opacity ?? (layer.style?.opacity ? Number.parseFloat(layer.style.opacity) : 1)

    return (
      <div
        key={layer.id}
        className={`absolute select-none ${
          !isActivelyDragging ? 'transition-opacity' : ''
        } ${
          isDragging && selectedLayerId === layer.id ? 'cursor-grabbing' : 'cursor-grab'
        } ${
          hasImageData ? '' : 'border-2 border-dashed'
        } ${
          isSelected ? 'ring-2 ring-primary ring-offset-2 border-primary' : hasImageData ? '' : 'border-border'
        }`}
        style={{
          left: `${layer.x}px`,
          top: `${layer.y}px`,
          width: `${layer.width}px`,
          height: `${layer.height}px`,
          opacity,
          willChange: isActivelyDragging ? 'transform' : 'auto',
        }}
        onClick={e => handleLayerClick(layer.id, e)}
        onMouseDown={e => handleMouseDown(layer.id, e)}
        data-testid={`image-layer-${layer.id}`}
      >
        {hasImageData
          ? (
              <img
                src={layer.imageData}
                alt={layer.description || layer.name || '图像'}
                className="w-full h-full object-cover pointer-events-none"
                draggable={false}
                style={{
                  opacity,
                }}
              />
            )
          : (
              <div
                className="flex flex-col items-center justify-center h-full p-2 text-center"
                style={{ backgroundColor: 'hsl(var(--muted))' }}
              >
                <ImageIcon className="w-8 h-8 text-muted-foreground mb-2" />
                <p className="text-xs text-muted-foreground line-clamp-3">
                  {layer.description || layer.content || '图像占位符'}
                </p>
              </div>
            )}
        {isSelected && (
          <>
            <div className="absolute -top-6 left-0 bg-primary text-primary-foreground text-xs px-2 py-1 rounded-md whitespace-nowrap font-medium z-10">
              {layer.name || '图像图层'}
            </div>
            {/* 调整大小控制点 */}
            <div
              className="absolute -bottom-1 -right-1 w-3 h-3 bg-primary border-2 border-background rounded-full cursor-se-resize z-10"
              onMouseDown={e => handleMouseDown(layer.id, e)}
            />
          </>
        )}
      </div>
    )
  }

  const renderLayer = (layer: Layer) => {
    if (layer.visible === false)
      return null

    switch (layer.type) {
      case 'text':
        return renderTextLayer(layer)
      case 'shape':
        return renderShapeLayer(layer)
      case 'image':
        return renderImageLayer(layer)
      default:
        return null
    }
  }

  return (
    <div
      ref={canvasContainerRef}
      className="relative flex-1 overflow-auto p-6"
      data-testid="canvas-area"
      onMouseMove={handleMouseMove}
      onMouseUp={handleMouseUp}
      onMouseLeave={handleMouseUp}
    >
      {/* Zoom Controls */}
      <div className="fixed top-20 right-100 z-10 flex gap-1 bg-card/80 backdrop-blur-sm rounded-lg border p-1">
        <Button
          size="icon"
          variant="ghost"
          onClick={handleZoomOut}
          disabled={zoom <= 0.25}
          data-testid="button-zoom-out"
          className="h-8 w-8"
        >
          <ZoomOut className="w-4 h-4" />
        </Button>
        <Button
          size="icon"
          variant="ghost"
          onClick={handleFitToScreen}
          data-testid="button-fit-screen"
          className="h-8 w-8"
        >
          <Maximize2 className="w-4 h-4" />
        </Button>
        <Button
          size="icon"
          variant="ghost"
          onClick={handleZoomIn}
          disabled={zoom >= 3}
          data-testid="button-zoom-in"
          className="h-8 w-8"
        >
          <ZoomIn className="w-4 h-4" />
        </Button>
        <div className="px-2 flex items-center justify-center min-w-12 text-xs font-mono text-muted-foreground">
          {Math.round(zoom * 100)}
          %
        </div>
      </div>

      {/* Canvas Content */}
      <div className="flex items-center justify-center min-h-full">
        <div
          className="relative"
          style={{
            transform: `scale(${zoom})`,
            transformOrigin: 'center',
            transition: 'transform 200ms ease-out',
          }}
        >
          {/* Canvas Background */}
          <div
            className="relative bg-white shadow-lg select-none"
            style={{
              width: `${canvasWidth}px`,
              height: `${canvasHeight}px`,
              backgroundImage:
                'repeating-conic-gradient(hsl(var(--muted)/0.3) 0% 25%, transparent 0% 50%)',
              backgroundSize: '20px 20px',
              backgroundPosition: '0 0, 10px 10px',
            }}
            onClick={() => onLayerSelect(null)}
            data-testid="canvas-background"
          >
            {/* Render all layers */}
            {layers.map(layer => renderLayer(layer))}
          </div>
        </div>
      </div>

      {/* 隐藏的 Canvas 用于导出 */}
      <canvas
        ref={canvasRef}
        className="hidden"
        style={{ display: 'none' }}
      />
    </div>
  )
})

Canvas.displayName = 'Canvas'
